import argparse
import random

import numpy as np
import logging


class Perceptron(object):

    def __init__(self, max_iter=5000, eta=0.00001, verbose=True):
        """
        感知机构造函数
        :param max_iter: 函数拟合的最大迭代次数
        :param eta: 学习率
        :param verbose: 是否输出迭代日志
        """
        self.eta_ = eta
        self.max_iter_ = max_iter
        self.w = 0
        self.verbose = verbose

    def fit(self, X, y):
        # tuple of array dimensions
        self.w = np.zeros(X.shape[1] + 1)
        n_iter_ = 0

        while n_iter_< self.max_iter_:

            # 用于生成指定范围内的随机整数。
            # random.randint(a, b)
            # a：指定随机数的下界（包含），即生成的随机整数可以取到的最小值。
            # b：指定随机数的上界（包含），即生成的随机整数可以取到的最大值。
            index = random.randint(0, y.shape[0] - 1)

            # 用于沿水平方向（水平堆叠）将两个或多个数组堆叠在一起，生成一个新的数组。
            # numpy.hstack(tup)
            # 参数说明：
            # tup：要堆叠的数组序列，可以是元组、列表或者数组的序列。
            # numpy.hstack()函数会将传入的数组序列沿着水平方向进行堆叠，即将每个数组的列按顺序依次排列在一起，生成一个新的数组。
            # 要求堆叠的数组序列中的数组的行数必须相同，否则会抛出 ValueError 异常。
            xx_ = np.hstack([X[index], 1])
            yy_ = 2 * y[index] - 1
            wx = np.dot(self.w, xx_)

            if wx * yy_ <= 0:
                self.w += self.eta_ * yy_ * xx_
            n_iter_ += 1
            if self.verbose:
                print(f'迭代次数{n_iter_}: 参数w为：{self.w}')


    def predict(self, X):
        # reshape(-1, 1)。其中 -1 表示 NumPy 应该自动计算这个维度的大小，以使得数组的总元素数保持不变。
        # 这个方法的作用是将原本的一维数组转换为一个列向量。
        X = np.hstack([X, np.ones(X.shape[0]).reshape((-1, 1))])

        rst = np.array([1 if rst else -1 for rst in np.dot(X, self.w) > 0])
        return rst


if __name__ == '__main__':
    # 日志设置
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)
    # 解析传入的命令行参数
    ap = argparse.ArgumentParser()
    ap.add_argument("-p", "--path", required=False, help="path to input data file")

    # vars() 函数用于返回对象的 __dict__ 属性，如果没有提供参数，则返回当前局部作用域的 __dict__。
    # 在 Python 中，大多数对象都有一个 __dict__ 属性，它是一个字典，包含了对象的属性和值。vars() 函数可以方便地访问这个字典，从而获取对象的属性和值。
    args = vars(ap.parse_args())

else:
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    logger = logging.getLogger(__name__)